<html>
<head lang="zh-CN">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>五子棋</title>
<style>
body {
	margin: 0px;
	padding: 0px;
}
body>div {
	display: inline-block;
	vertical-align: top;
}
.board {
	width: 540px;
}
.board>div {
	display: inline-block;
	width: 36px;
	height: 36px;
	font-size: 37px;
	/*font-family: "MS ゴシック"*/
}
.buttons {
	margin-top: 24px;
	width:150px;
}
.buttons button {
	margin-bottom: 10px;
	width: 120px;
}
.cells {
	position: relative;;
}
.stones {
	top: 7px;
	left: 2px;
	position: absolute;
	border-radius: 50%;
	width: 31px;
	height: 31px;
}

/*
┌┬┐
├┼┤
└┴┘
 */
.first-left-cell::after {
	content: "┌"
}
.first-right-cell::after {
	content: "┐"
}
.first-line-cell::after {
	content: "┬"
}
.left-line-cell::after {
	content: "├"
}
.inner-cell::after {
	content: "┼"
}
.right-line-cell::after {
	content: "┤"
}
.last-left-cell::after {
	content: "└"
}
.last-right-cell::after {
	content: "┘"
}
.last-line-cell::after {
	content: "┴"
}
.black-stone {
	box-shadow: 2px 2px 3px #444444;
	background-image: radial-gradient(
		farthest-corner at 12px 8px,
		#5E5757, #000000
	);
}
.white-stone {
	box-shadow: 4px 3px 3px #444444;
	background-image: radial-gradient(
		farthest-corner at 12px 8px,
		#ECECEC, #5A5A5A
	);
}
</style>
</head>
<body>
	<div id="board"></div>
	<div class="buttons">
		<button id="first-turn-btn" onclick="startGame(true)">先手（黑子）</button>
		<button id="second-turn-btn" onclick="startGame(false)">后手（白子）</button>
		<button id="be-fall-btn" disabled="disabled" onclick="beFall()">认输</button>
	</div>
</body>
<script>
const firstTurnBtnNode = document.getElementById("first-turn-btn");
const secondTurnBtnNode = document.getElementById("second-turn-btn");
const beFallBtnNode = document.getElementById("be-fall-btn");
const BOARD = document.getElementById("board");

const BLACK_COLOR = 1, WHITE_COLOR = 2, BOARD_SIZE = 15, FIVE = 5;
let el = null, cells = null;
let baseBoard = null, blackBoard = null, whiteBoard = null, times,
	isStarted  = false, firstTurn = false, personTurn = false; // 先手：黑；后手：白

function createBoard () {
	times = true;
	cells = [];
	baseBoard = [];
	blackBoard = [];
	whiteBoard = [];

	for (let i = 0; i < BOARD_SIZE; i++) {
		baseBoard.push(new Array(BOARD_SIZE).fill(0));
		blackBoard.push(new Array(BOARD_SIZE).fill(0));
		whiteBoard.push(new Array(BOARD_SIZE).fill(0));
		cells.push(new Array(BOARD_SIZE).fill(null));
	}
}

function  drawBoard() {
	createBoard();

	if (el !== null) {
		BOARD.removeChild(el);
		el = null;
	}
	el = document.createElement("div");
	el.className = "board";

	for(let i=0; i < BOARD_SIZE; i++){
		for (let j=0; j < BOARD_SIZE; j++){
			const bgCell= document.createElement("div");
			const cell = document.createElement("div");

			if (i === 0) {
				if (j === 0) {
					bgCell.className = "cells first-left-cell";
				} else if (j === BOARD_SIZE - 1) {
					bgCell.className = "cells first-right-cell";
				} else {
					bgCell.className = "cells first-line-cell";
				}
			} else if (i === BOARD_SIZE - 1) {
				if (j === 0) {
					bgCell.className = "cells last-left-cell";
				} else if (j === BOARD_SIZE - 1) {
					bgCell.className = "cells last-right-cell";
				} else {
					bgCell.className = "cells last-line-cell";
				}
			} else {
				if (j === 0) {
					bgCell.className = "cells left-line-cell";
				} else if (j === BOARD_SIZE - 1) {
					bgCell.className = "cells right-line-cell";
				} else {
					bgCell.className = "cells inner-cell";
				}
			}

			bgCell.onclick = () => {

				if (!isStarted) {
					alert("请先选择先后手开局！");
					return;
				}

				const color = baseBoard[i][j] = times ? BLACK_COLOR : WHITE_COLOR;
				const myBoard = personTurn ? blackBoard : whiteBoard;
				const enemyBoard = !personTurn ? blackBoard : whiteBoard;
				cell.className = times ? "stones black-stone" : "stones white-stone";

				setTimeout(() => {
					personTurn = !personTurn;
					if (checkStatus(i, j , times)) {
						alert(`${times ? '黑' : '白'}子赢了！`);
					} else {
						times = !times;
						putStone(i, j, myBoard, enemyBoard, color);
						if (!personTurn) {
							newStone();
						}
					}
				}, 100);
			};

			cells[i][j] = bgCell;
			bgCell.appendChild(cell);
			el.appendChild(bgCell);
		}
	}

	BOARD.appendChild(el);
}

function checkStatus(i, j, times) {
	const point = [i, j];

	if (checkStatusHorizon(point, times)) return true;
	if (checkStatusVertical(point, times)) return true;
	if (checkStatusSlash(point, times)) return true;
	if (checkStatusBackSlash(point, times)) return true;

	return false;
}

const toLeft = ([y, x], i = 1) => [y, x - i],
			toRight = ([y, x], i = 1) => [y, x + i],
			toUp = ([y, x], i = 1) => [y - i, x],
			toDown = ([y, x], i = 1) => [y + i, x],
			toRightUp = ([y, x], i = 1) => [y + i, x - i],
			toLeftDown = ([y, x], i = 1) => [y - i, x+ i],
			toLeftUp  = ([y, x], i = 1) => [y - i, x - i],
			toRightDown = ([y, x], i = 1) => [y + i, x + i];
const checkStatusHorizon = createCheck(toLeft, toRight),
			checkStatusVertical = createCheck(toUp, toDown),
			checkStatusSlash = createCheck(toRightUp, toLeftDown),
			checkStatusBackSlash = createCheck(toLeftUp, toRightDown);

function createCheck(p1Mov, p2Mov) {
	return function (point, times) {
		const tmpBoard = times ? blackBoard : whiteBoard;
		let p1 = p1Mov(point);
		let p2 = p2Mov(point);
		let [i, j] = point;
		let count = 1;
		for (let index = 0; index < 4; index++) {
			let p1IsValid = false, p2IsValid = false;
			if (isValid(p1, times)) {
				count++;
				p1 = p1Mov(p1);
				p1IsValid = true;
			}
			if (isValid(p2, times)) {
				count++;
				p2 = p2Mov(p2);
				p2IsValid = true;
			}
			if (!p1IsValid && !p2IsValid) return false;

			if (count >= FIVE) {
				isStarted  = false;
				return true;
			}
		}
	}
}

function putStone(y, x, myBoard, enemyBoard, color) {
	/*
	 * 每次落子，都把棋盘中的分数全部更新
	 * 有子的位置（不分敌我） => 0
	 * 如果当前的位置是空，则向周围扩散判分
	 * 如果周围位置也是空，则停止判断
	 * 周围有和当前颜色的一致的子时，继续判断
	 * 算出周围 8 条路线上的全部得分，并取最高分
	 */
	for (let i = 0; i < BOARD_SIZE; i++) {
		for (let j = 0; j < BOARD_SIZE; j++) {
			const cell = baseBoard[i][j];
			if (cell === 0) {
				let hCount = 0, vCount = 0, sCount = 0, bsCount = 0;

				// —
				for (let c = 1; c < FIVE; c++) {
					const [li, lj] = toLeft([i, j], c);
					if (isInBoard(li, lj)) {
						if(baseBoard[li][lj] !== color) break;
						hCount++;
					}
				}
				for (let c = 1; c < FIVE; c++) {
					const [ri, rj] = toRight([i, j], c);
					if (isInBoard(ri, rj)) {
						if(baseBoard[ri][rj] !== color) break;
						hCount++;
					}
				}
				// |
				for (let c = 1; c < FIVE; c++) {
					const [ui, uj] = toUp([i, j], c);
					if (isInBoard(ui, uj)) {
						if(baseBoard[ui][uj] !== color) break;
						vCount++;
					}
				}
				for (let c = 1; c < FIVE; c++) {
					const [di, dj] = toRight([i, j], c);
					if (isInBoard(di, dj)) {
						if(baseBoard[di][dj] !== color) break;
						vCount++;
					}
				}
				// \
				for (let c = 1; c < FIVE; c++) {
					const [lui, luj] = toLeftUp([i, j], c);
					if (isInBoard(lui, luj)) {
						if(baseBoard[lui][luj] !== color) break;
						bsCount++;
					}
				}
				for (let c = 1; c < FIVE; c++) {
					const [rdi, rdj] = toRightDown([i, j], c);
					if (isInBoard(rdi, rdj)) {
						if(baseBoard[rdi][rdj] !== color) break;
						bsCount++;
					}
				}
				// /
				for (let c = 1; c < FIVE; c++) {
					const [rui, ruj] = toRightUp([i, j], c);
					if (isInBoard(rui, ruj)) {
						if(baseBoard[rui][ruj] !== color) break;
						sCount++;
					}
				}
				for (let c = 1; c < FIVE; c++) {
					const [ldi, ldj] = toLeftDown([i, j], c);
					if (isInBoard(ldi, ldj)) {
						if(baseBoard[ldi][ldj] !== color) break;
						sCount++;
					}
				}

				const max = Math.max(hCount, vCount, sCount, bsCount);
				if (max) {
					myBoard[i][j] = Math.pow(FIVE, max);
				}
			} else {
				myBoard[i][j] = 0;
			}
		}
	}

	enemyBoard[y][x] = 0;
}

function  isValid([i, j], times) {
	if (!isInBoard(i, j)) return false;
	const color = baseBoard[i][j];
	if (color === 0) return false;
	return color === (times ? BLACK_COLOR : WHITE_COLOR);
}

function isInBoard(i, j) {
	if (i < 0 || j < 0) return false;
	if ((i > BOARD_SIZE - 1 || (j > BOARD_SIZE -1))) return false;
	return true;
}

function newStone(times) {
	const myBoard = times? blackBoard : whiteBoard;
	const enemyBoard = !times? blackBoard : whiteBoard;

	function  getMax(board) {
		const maxes = [];
		const ijList = [];
		for (let i = 0; i < BOARD_SIZE; i++) {
			for (let j = 0; j < BOARD_SIZE; j++) {
				maxes.push(board[i][j]);
				ijList.push([i, j]);
			}
		}

		const max = Math.max(... maxes);
		const index = maxes.indexOf(max);
		const position = ijList[index];

		return { max, position };
	}

	const me = getMax(myBoard);
	const enemy = getMax(enemyBoard);

	let position;
	if ((me.max === 625) || (me.mex === 125 && enemy.max < 125)) {
		position = me.position;
	} else {
		position = enemy.position;
	}

	cells[position[0]][position[1]].onclick();
}

drawBoard();

function startGame(times) {
	
	if (isStarted ) {
		alert("请勿重复开局");
		return;
	}

	firstTurnBtnNode.disabled = "disabled";
	secondTurnBtnNode.disabled = "disabled";
	beFallBtnNode.disabled = "";
	isStarted  = true;
	firstTurn = times;
	personTurn = times;

	if (!times) {
		let x = Math.floor(Math.random() * BOARD_SIZE),
			y = Math.floor(Math.random() * BOARD_SIZE);

		const append = [2, 3, 4];

		if (x < 7) {
			const fix = Math.floor(Math.random()* append.length);
			x = 7 + append[fix];
		}
		if (x > 11) {
			const fix = Math.floor(Math.random()* append.length);
			x = 11 - append[fix];
		}
		if (y < 7) {
			const fix = Math.floor(Math.random()* append.length);
			y = 7 + append[fix];
		}
		if (y > 11) {
			const fix = Math.floor(Math.random()* append.length);
			y = 11 - append[fix];
		}

		cells[y][x].onclick();
	}
}
function beFall () {
	isStarted  = false;
	alert(`${firstTurn ? '黑': '白'}子赢了`);

	firstTurnBtnNode.disabled = "";
	secondTurnBtnNode.disabled = "";
	beFallBtnNode.disabled = "disabled";
}
</script>
</html>